package com.shawn.springboot.integeration.netty.client.factory;

import com.shawn.springboot.integeration.netty.client.IClient;
import com.shawn.springboot.integeration.netty.client.NettyClient;
import com.shawn.springboot.integeration.netty.client.initializer.factory.InitializerFactory;
import com.shawn.springboot.integeration.netty.exception.ApiException;
import com.shawn.springboot.integeration.netty.model.ServerAddress;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.internal.SystemPropertyUtil;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;

/**
 * @author zhangjian
 * @title: NettyClientFactory
 * @projectName 12-springboot学习
 * @description: Netty实现的客户端工厂
 * @date 2021/4/2714:45
 */
@Slf4j
public class NettyClientFactory extends AbstractClientFactory{
    private static final String BYTE_BUF_POOL_NAME = "bytebuf.pool";
    private static final String IO_RATIO_NAME = "ioratio";
    public static final NioEventLoopGroup WORKER_GROUP = new NioEventLoopGroup();
    public static final ByteBufAllocator BYTE_BUF_ALLOCATOR;

    static {
        WORKER_GROUP.setIoRatio(SystemPropertyUtil.getInt(IO_RATIO_NAME, 100));
        if (SystemPropertyUtil.getBoolean(BYTE_BUF_POOL_NAME, false)) {
            BYTE_BUF_ALLOCATOR = PooledByteBufAllocator.DEFAULT;
        } else {
            BYTE_BUF_ALLOCATOR = UnpooledByteBufAllocator.DEFAULT;
        }
    }

    @Override
    protected IClient createClient(ServerAddress address) throws ApiException {
        //启动引导类
        final Bootstrap bootstrap = new Bootstrap();
        //绑定工作线程组
        bootstrap.group(NettyClientFactory.WORKER_GROUP);
        //设置低延迟
        bootstrap.option(ChannelOption.TCP_NODELAY, true);
        //设置让关闭的的端口尽早可以使用
        bootstrap.option(ChannelOption.SO_REUSEADDR, true);
        //若是长连接,开启SOCKET默认的心跳机制,短连接则不不开启
        bootstrap.option(ChannelOption.SO_KEEPALIVE, address.isLongConnection());
        //使用内存池
        bootstrap.option(ChannelOption.ALLOCATOR, NettyClientFactory.BYTE_BUF_ALLOCATOR);
        //设置IO类型
        bootstrap.channel(NioSocketChannel.class);
        //设置初始化连接配置
        bootstrap.handler(InitializerFactory.get(address));
        //若是设置的连接超时时间
        bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, address.getConnectTimeout());

        String targetIp = address.getIp();
        int targetPort = address.getPort();
        ChannelFuture future = bootstrap.connect(new InetSocketAddress(targetIp, targetPort));
        if (future.awaitUninterruptibly(address.getConnectTimeout()) && future.isSuccess() && future.channel().isActive()) {
            log.info("[{}]连接成功,服务器端地址[{}:{}]",targetIp,targetPort);
            Channel channel = future.channel();
            return new NettyClient(channel,address);
        } else {
            future.cancel(true);
            future.channel().close();
            StringBuilder errorMsg = new StringBuilder();
            errorMsg.append("服务器[").append(targetIp).append(":").append(targetPort).append("]连接失败");
            throw new ApiException(errorMsg.toString());
        }
    }
}
